import { PureComponent } from 'react';
import {
  Scene, OrthographicCamera, BoxGeometry, Line, Vector3,
  MeshLambertMaterial, Mesh, WebGLRenderer, DirectionalLight,
  MeshBasicMaterial, PlaneGeometry, TextureLoader, RepeatWrapping,
  SphereGeometry
} from 'three';
import Stats from 'stats.js';
import { requestAnimationFrame } from '@/utils/requestAnimationFrame';

//画坐标轴的方法
const drawAxes = scene => {
  const xGeo = new BoxGeometry();
  const xGeoPoints = [
    new Vector3(0, 0, 0),
    new Vector3(3, 0, 0)
  ];
  xGeo.setFromPoints(xGeoPoints);
  const xMat = new MeshBasicMaterial({
    color: 0xff0000
  });
  const xAxis = new Line(xGeo, xMat);
  scene.add(xAxis);

  const yGeo = new BoxGeometry();
  const yGeoPoints = [
    new Vector3(0, 0, 0),
    new Vector3(0, 3, 0)
  ];
  yGeo.setFromPoints(yGeoPoints);
  const yMat = new MeshBasicMaterial({
    color: 0x00ff00
  });
  const yAxis = new Line(yGeo, yMat);
  scene.add(yAxis);

  const zGeo = new BoxGeometry();
  const zGeoPoints = [
    new Vector3(0, 0, 0),
    new Vector3(0, 0, 3)
  ];
  zGeo.setFromPoints(zGeoPoints);
  const zMat = new MeshBasicMaterial({
    color: 0x00ccff
  });
  const zAxis = new Line(zGeo, zMat);
  scene.add(zAxis);
};

//场景
const scene = new Scene();

//画坐标轴
drawAxes(scene);

//相机
const camera = new OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100);
camera.position.set(5, 10, 20);
camera.lookAt(0, 3, 0);

//光源
const light = new DirectionalLight(0xffffff);
light.position.set(10, 10, 15);
scene.add(light);

//渲染器
let renderer = null;

//球
let ball = null;

//球半径
const ballRadius = 0.5;

//请求id
let animateId = 0;

//stats实例
const stats = new Stats();

//只在竖直方向上做自由落体运动, 因此位置变量省去, 就只有速度 加速度两个变量
//速度
let v = 0;

//加速度(表示每帧, 球体向y轴负方向移动0.1像素)
const a = -0.01;

//是否在运动
let isMoving = false;

//最大高度
//这里我让球体一开始就在最顶端, 点击按钮开始下落, 和教程中的示例有些出入
const maxHeight = 5;

//球体
const ballGeo = new SphereGeometry(ballRadius, 16, 8);
//使用了光照, 那么材质的选择就不能使用MeshBasicMaterial了
const ballMat = new MeshLambertMaterial({
  color: 0xffff00
});
ball = new Mesh(ballGeo, ballMat);
//所有几何体的几何中心都是在原点, 因此这需要沿着y轴正方向移动它的半径的距离,
//使得球体的底部位于"地面"之上, 也就是说: 这个位置说的是几何中心的位置
ball.position.y = ballRadius;
scene.add(ball);

//动画
const animate = () => {
  if(!isMoving) {
    stopAnimate();
    return;
  }
  stats.begin();
  //移动中
  if(isMoving) {
    //起始速度为零, 然后距离从最高开始一点一点减少
    //也就是说位置的变化是一点点变的, 因此是一个+=的操作
    //而速度又受到加速度的影响, 也是一点点变得
    //速度变化
    ball.position.y += v;
    v += a;
    //碰撞检测
    if(ball.position.y <= ballRadius) {
      //此时球体与地面产生碰撞
      //让速度变为正, 那么球体就会向上运动, 产生弹跳运动
      //同时参照例子, 乘以一个系数
      v = -v * 0.9;
    }

    if(Math.abs(v) < 0.001) {
      //停止移动
      isMoving = false;
    }

  }
  renderer.render(scene, camera);
  stats.end();
  animateId = requestAnimationFrame(animate);

};

//开始动画
const startAnimate = () => {
  if(animateId) return;
  //启动动画时将速度重置为零,
  //以防运动结束再次启动运动异常
  v = 0;
  ball.position.y = maxHeight;
  isMoving = true;
  animateId = requestAnimationFrame(animate);
};

//停止动画
const stopAnimate = () => {
  if(!animateId) return;
  cancelAnimationFrame(animateId);
  animateId = 0;
};

class Index extends PureComponent {

  async componentDidMount() {
    //渲染器需要访问DOM节点, 因此只能在这个生命周期中写
    renderer = new WebGLRenderer({
      canvas: document.getElementById('mainCanvas')
    });
    //导入图像作为纹理贴图，并添加到相应的材质中(加载外部图片作为材质渲染平面)
    const textureLoader = new TextureLoader();
    const texture = await textureLoader.loadAsync('/img/chess.png');
    texture.wrapS = texture.wrapT = RepeatWrapping;
    texture.repeat.set(4, 4);
    const planeGeo = new PlaneGeometry(5, 5);
    const planeMat = new MeshLambertMaterial({
      map: texture
    });
    const plane = new Mesh(planeGeo, planeMat);
    //将平面沿x轴做一个旋转, 使得它看起来处于y z轴平面, 更像一个"地面"
    plane.rotation.x = -Math.PI / 2;
    scene.add(plane);

    const fpsContainer = document.getElementById('fps-container');
    stats.domElement.style.position = '';
    fpsContainer.appendChild(stats.dom);
    
    renderer.render(scene, camera);
    
  }

  render() {

    return (
      <div>
        <div>
          <button
            onClick={startAnimate}
          >开始</button>
          <button
            onClick={stopAnimate}
          >停止</button>
        </div>
        <div
          id="fps-container"
          style={{
            display: 'flex',
            padding: '5px'
          }}
        />
        <canvas
          id="mainCanvas"
          width="400"
          height="300"
        />
      </div>
    );
  }
}

export default Index;